from PyQt6.QtWidgets import * 
from PyQt6.QtGui import *
from PyQt6.QtCore import *
from GUI.WorkSpace import Ui_Form as WorkSpaceUi
from Utils.image_precesser import Graph, GRID
from multiprocessing import Process, Queue
import GUI.icons
import os
import cv2
import uuid
import time
import numpy as np


level_list = ["正常汇管区", "汇管区纤维化", "汇管区纤维化趋势", "纤维间隔", "纤维间隔趋势", "桥接", "桥接趋势", "假小叶", "假小叶趋势"]

color_table = dict()
color_table["正常汇管区"] = QColor(0, 59, 0)
color_table["汇管区纤维化"] = QColor(255, 0, 0)
color_table["汇管区纤维化趋势"] = QColor(255, 255, 0)
color_table["纤维间隔"] = QColor(0, 255, 255)
color_table["纤维间隔趋势"] = QColor(0, 180, 120)
color_table["桥接"] = QColor(0, 255, 0)
color_table["桥接趋势"] = QColor(255, 150, 50)
color_table["假小叶"] = QColor(0, 0, 255)
color_table["假小叶趋势"] = QColor(255, 0, 200)



def clamp(value, min_value, max_value):
    return max(min_value, min(value, max_value))

def qImage_to_npArray(image:QImage):
    width = image.width()
    height = image.height()
    ptr = image.bits()
    byteCount = image.bytesPerLine() * height
    ptr.setsize(byteCount)
    arr = np.array(ptr).reshape(height, width, 4)
    arr = cv2.cvtColor(arr, cv2.COLOR_RGBA2BGRA)
    return arr

def npArray_to_qImage(np_array:np.ndarray):
    # 确保NumPy数组是uint8类型
    if np_array.dtype != np.uint8:
        np_array = np_array.astype(np.uint8)

    # 获取图像的高度、宽度和通道数
    height, width, channels = np_array.shape

    # QImage的字节行宽应该包括所有通道，即使它们包含透明度
    bytes_per_line = channels * width

    # 根据通道数创建正确的QImage格式
    if channels == 4:
        qimage_format = QImage.Format.Format_RGBA8888
    elif channels == 3:
        qimage_format = QImage.Format.Format_BGR888
    else:
        raise ValueError("Unsupported number of channels")

    # 创建QImage
    qimage = QImage(np_array.data.tobytes(), width, height, bytes_per_line, qimage_format)

    # 确保数据被复制，否则可能在NumPy数组被释放时导致问题
    qimage = qimage.copy()

    return qimage

def change_color(pixmap:QPixmap, color:QColor):
    if pixmap.isNull():
        return QPixmap()
    # 将QPixmap转换为QImage
    image = pixmap.toImage()
    # 将QImage转换为numpy数组
    arr = qImage_to_npArray(image)
    # 拆分RGB和Alpha通道
    rgb = arr[:,:,:3]
    alpha = arr[:,:,3]
    # print(f"rgb shape: {rgb.shape}, alpha shape: {alpha.shape}")
    # 获取非零Alpha通道的掩码
    non_zero_alpha_mask = alpha > 0
    # 修改RGB通道
    rgb[non_zero_alpha_mask] = color.getRgb()[:3]
    # 重新组合
    new_arr = np.concatenate([rgb, alpha[:,:,np.newaxis]], axis=2)
    # 将numpy数组转换为QImage
    new_image = npArray_to_qImage(new_arr)
    # 将QImage转换为QPixmap
    new_pixmap = QPixmap.fromImage(new_image)
    return new_pixmap

def layer_map_process(input_queue, output_queue):
    '''
    传入字典：{idx, origin_pixmap, offset, scale, map_pixmap}
    返回字典：{idx, origin_pixmap}
    '''
    while True:
        if not input_queue.empty():
            layer_map = input_queue.get()
            idx = layer_map['idx']
            if idx == -1:
                break
            origin_pixmap = QPixmap(layer_map['origin_pixmap'])
            offset = layer_map['offset']
            scale = layer_map['scale']
            map_pixmap = QPixmap(layer_map['map_pixmap'])
            origin_matrix = qImage_to_npArray(origin_pixmap.toImage())
            map_matrix = qImage_to_npArray(map_pixmap.toImage())
            offset_col = int(offset[0] * scale)
            offset_row = int(offset[1] * scale)
            origin_matrix[offset_row:offset_row + map_matrix.shape[0], offset_col:offset_col + map_matrix.shape[1]] = map_matrix
            origin_pixmap = QPixmap.fromImage(npArray_to_qImage(origin_matrix))
            output_queue.put({'idx': idx, 'origin_pixmap': Pickable_QPixmap(origin_pixmap)})
            print(f"process: {idx}")
        else:
            time.sleep(0.2)

def dContoursIsClose(image_matrix)-> bool:
    contours, hierarchy = cv2.findContours(image_matrix, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
    # print(f"hierarchy: {hierarchy}")
    hierarchy = hierarchy[0]
    for i in range(len(hierarchy)):
        if hierarchy[i][3] == -1:
            if hierarchy[i][2] == -1:
                return False
    return True

class Pickable_QPixmap(QPixmap):
    def __reduce__(self):
        return type(self), (), self.__getstate__()

    def __getstate__(self):
        ba = QByteArray()
        stream = QDataStream(ba, QIODevice.OpenModeFlag.WriteOnly)
        stream << self
        return ba

    def __setstate__(self, ba):
        stream = QDataStream(ba, QIODevice.OpenModeFlag.ReadOnly)
        stream >> self  


class CustomListItem(QWidget):
    signal = pyqtSignal(uuid.UUID)
    change_type_signal = pyqtSignal(uuid.UUID, str)
    def __init__(self, name, layer_num=-1, parent=None, receiver=None, type_name=None):
        super(CustomListItem, self).__init__(parent=parent)

        self.layout = QVBoxLayout(self)
        self.layer_num = layer_num
        # 创建一个名字标签
        self.name_label = QLabel(name)
        # 居中
        self.name_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.layout.addWidget(self.name_label)

        # 创建一个下拉列表
        self.combo_box = QComboBox()
        self.combo_box.addItems(level_list)  # 这里是病理分级的文字选项
        if type_name is not None:
            self.combo_box.setCurrentText(type_name)
        self.combo_box.currentIndexChanged.connect(self.on_combo_box_changed)
        self.layout.addWidget(self.combo_box)
        # 创建一个删除按钮
        self.delete_button = QPushButton("删除")
        self.delete_button.setObjectName("delete_button")
        self.layout.addWidget(self.delete_button)
        self.signal.connect(receiver.on_signal_received)
        self.change_type_signal.connect(receiver.on_change_type_signal)
        self.delete_button.clicked.connect(self.on_delete_button_clicked)

    def on_delete_button_clicked(self):
        self.signal.emit(self.layer_num)

    def on_combo_box_changed(self, index):
        self.change_type_signal.emit(self.layer_num, self.combo_box.currentText())

    def set_type(self, type_name):
        # 不触发信号
        self.combo_box.blockSignals(True)
        self.combo_box.setCurrentText(type_name)
        self.combo_box.blockSignals(False)



class DrawLayer:
    def __init__(self, width, height, unique_id, pen_width, erase_width):
        # self.canvas = QPixmap(width, height)
        # self.canvas.fill(Qt.GlobalColor.transparent)
        self.width = width
        self.height = height
        # 用归一化坐标系表示画布的起点和终点
        # self.pixItem = QGraphicsPixmapItem(self.canvas)
        self.pixItem_list = []
        self.layer_maps = []
        self.layer_transforms = []
        self.last_pos = None
        self.id = unique_id
        self.type = "正常汇管区"
        self.erase_map = None
        self.last_erase_pos = None
        self.pen_width = pen_width
        self.erase_width = erase_width

    def transform(self,scene,new_width, new_height,length_width, length_height, left_top, layer_num, queue=None):
            
        new_pixItem_list = []
        for i, pixItem in enumerate(self.pixItem_list):
            scene.removeItem(pixItem)
            origin_layer_num = self.layer_maps[i]["layer_num"]
            pixmap = self.layer_maps[i]["pixmap"]
            my_left_top = self.layer_maps[i]["left_top"]
            scale = 2 ** (origin_layer_num - layer_num)
            # print(f"pen scale: {scale}, length_width: {length_width}, length_height: {length_height}")
            width1  = pixmap.width() * scale
            height1 = pixmap.height() * scale
            my_right_bottom = (my_left_top[0] + width1 / length_width, my_left_top[1] + height1/ length_height)
            right_bottom = (left_top[0] + new_width / length_width, left_top[1] + new_height / length_height)
            # print(f"my_left_top: {my_left_top}, my_right_bottom: {my_right_bottom}")
            # print(f"left_top: {left_top}, right_bottom: {right_bottom}")
            my_left_top_x = clamp(my_left_top[0], left_top[0], right_bottom[0])
            my_left_top_y = clamp(my_left_top[1], left_top[1], right_bottom[1])
            my_right_bottom_x = clamp(my_right_bottom[0], left_top[0], right_bottom[0])
            my_right_bottom_y = clamp(my_right_bottom[1], left_top[1], right_bottom[1])
            clamp_left_top = (my_left_top_x, my_left_top_y)
            clamp_right_bottom = (my_right_bottom_x, my_right_bottom_y)
            x = (clamp_left_top[0] - my_left_top[0]) * length_width
            y = (clamp_left_top[1] - my_left_top[1]) * length_height
            self.layer_transforms[i]['offset'] = (x, y)
            self.layer_transforms[i]['scale'] = 1 / scale
            clamp_width = (clamp_right_bottom[0] - clamp_left_top[0]) * length_width
            clamp_height = (clamp_right_bottom[1] - clamp_left_top[1]) * length_height
            # print(f"clamp_left_top: {clamp_left_top}, clamp_right_bottom: {clamp_right_bottom}")
            # print(f"x: {x}, y: {y}, clamp_width: {clamp_width}, clamp_height: {clamp_height}")
            new_pixmap = pixmap.scaled(int(width1), int(height1))
            if clamp_width == 0 or clamp_height == 0:
                empty_pixItem = QGraphicsPixmapItem(QPixmap(1, 1))
                empty_pixItem.setZValue(1)
                empty_pixItem.setPos(x, y)
                new_pixItem_list.append(empty_pixItem)
            else:
                clamp_pixmap = new_pixmap.copy(int(x), int(y), int(clamp_width), int(clamp_height))
                pos_x = (clamp_left_top[0] - left_top[0]) * length_width    
                pos_y = (clamp_left_top[1] - left_top[1]) * length_height
                new_pixItem = QGraphicsPixmapItem(clamp_pixmap)
                new_pixItem.setZValue(1)
                new_pixItem.setPos(pos_x, pos_y)
                new_pixItem_list.append(new_pixItem)
            
        self.pixItem_list = new_pixItem_list
        self.width = new_width
        self.height = new_height
    
    def paint(self, pos, scene: QGraphicsScene, scale):       
        if self.last_pos is not None:
            scene.removeItem(self.pixItem_list[-1])
            # 取出最后一张canvas
            last_canvas = self.pixItem_list[-1].pixmap()
            painter = QPainter(last_canvas)
            painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
            width = self.pen_width / scale
            painter.setPen(QPen(color_table[self.type], width))
            painter.drawLine(self.last_pos, pos)
            painter.end()
            # 更新pixItem
            pixItem = QGraphicsPixmapItem(last_canvas)
            pixItem.setZValue(1)
            self.pixItem_list[-1] = pixItem
            scene.addItem(pixItem)
            self.last_pos = pos
        else:
            # 创建新的画布
            canvas = QPixmap(self.width, self.height)
            canvas.fill(Qt.GlobalColor.transparent)
            painter = QPainter(canvas)
            painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
            width = self.pen_width / scale
            painter.setPen(QPen(color_table[self.type], width))
            painter.drawPoint(pos)
            # 更新pixelItem
            pixItem = QGraphicsPixmapItem(canvas)
            pixItem.setZValue(1)
            self.pixItem_list.append(pixItem)
            scene.addItem(pixItem)
            self.last_pos = pos


    def change_type(self, type_name, scene):
        # 删除老的
        for pixItem in self.pixItem_list:
            scene.removeItem(pixItem)
        # 根据type_name修改画笔颜色
        self.type = type_name
        for i, pixItem in enumerate(self.pixItem_list):
            # 提取QPixmap
            pixmap = pixItem.pixmap()
            left_top = pixItem.pos()
            new_pixmap = change_color(pixmap, color_table[self.type])
            pixItem = QGraphicsPixmapItem(new_pixmap)
            pixItem.setZValue(1)
            pixItem.setPos(left_top)
            scene.addItem(pixItem)
            self.pixItem_list[i] = pixItem
            # 更新layer_map
            pixmap = self.layer_maps[i]["pixmap"]
            new_pixmap = change_color(pixmap, color_table[self.type])
            self.layer_maps[i]["pixmap"] = new_pixmap
            

    def find_layer_idx(self, pos):
        idx_list = []
        for i, pixItem in enumerate(self.pixItem_list):
            left_top = pixItem.pos()
            image = pixItem.pixmap().toImage()
            width = image.width()
            height = image.height()
            local_pos = QPointF(pos[0], pos[1]) - left_top
            if local_pos.x() < 0 or local_pos.x() >= width or local_pos.y() < 0 or local_pos.y() >= height:
                continue
            pixel_color = image.pixelColor(int(local_pos.x()), int(local_pos.y()))
            # print(f'left_top:{left_top.x()} {left_top.y()}, pos: {pos}, pixel_color: {pixel_color}')
            if pixel_color.alpha() > 0:
                idx_list.append(i)

        return idx_list
            
    
    def erase(self, pos, scene, scale, queue, erase_type):
        if self.last_erase_pos is not None:
            painter = QPainter(self.erase_map)
            painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
            width = self.erase_width / scale
            painter.setPen(QPen(color_table[self.type], width))
            painter.drawLine(self.last_erase_pos, pos)
            painter.end()
            self.last_erase_pos = pos
        else:
            # 纯白画布
            self.erase_map = QPixmap(self.width, self.height)
            self.erase_map.fill(Qt.GlobalColor.transparent)
            painter = QPainter(self.erase_map)
            painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
            width = self.erase_width / scale
            painter.setPen(QPen(color_table[self.type], width))
            painter.drawPoint(pos)
            painter.end()
            self.last_erase_pos = pos
        # numpy
        erase_matrix = qImage_to_npArray(self.erase_map.toImage())
        for i, pixItem in enumerate(self.pixItem_list):
            scene.removeItem(pixItem)
            left_top = pixItem.pos()
            image = pixItem.pixmap().toImage()
            # 打印最后一个像素的颜色
            image_matrix = qImage_to_npArray(image)
            if image_matrix.shape[0] == 1 and image_matrix.shape[1] == 1:
                continue
            # print(f"image_matrix shape: {image_matrix.shape}")
            sub_matrix_alpha = erase_matrix[int(left_top.y()):int(left_top.y()) + image_matrix.shape[0], int(left_top.x()):int(left_top.x()) + image_matrix.shape[1], 3]
            if erase_type == "不完整的":
                not_zero_alpha_pos = np.where(sub_matrix_alpha > 0)
                image_matrix[not_zero_alpha_pos[0], not_zero_alpha_pos[1],3] = 0  
            elif erase_type == "完整的":
                element = image_matrix[:,:,3].copy()
                and_map = cv2.bitwise_and(element, sub_matrix_alpha)
                not_zero_alpha_pos = np.where(and_map > 0)
                print(f"i: {i}, not_zero_alpha_pos: {len(not_zero_alpha_pos[0])}")
                if len(not_zero_alpha_pos[0]) != 0:
                    image_matrix[:, :,3] = 0
            new_pixmap = QPixmap.fromImage(npArray_to_qImage(image_matrix))
            new_pixItem = QGraphicsPixmapItem(new_pixmap)
            new_pixItem.setZValue(1)
            new_pixItem.setPos(left_top)
            scene.addItem(new_pixItem)
            self.pixItem_list[i] = new_pixItem

    def update_layer_map(self):
        print(f"layer_maps: {len(self.layer_maps)}, pixItem_list: {len(self.pixItem_list)}")
        for i, pixItem in enumerate(self.pixItem_list):
            origin_pixmap = self.layer_maps[i]["pixmap"]
            origin_matrix = qImage_to_npArray(origin_pixmap.toImage())
            scale = self.layer_transforms[i]['scale']
            offset = self.layer_transforms[i]['offset']
            map_pixmap = pixItem.pixmap().copy()
            print(f"map_pixmap: {map_pixmap.width()}, {map_pixmap.height()}, scale: {scale}")
            if map_pixmap.width() == 1 and map_pixmap.height() == 1:
                continue
            map_pixmap = map_pixmap.scaled(int(map_pixmap.width() * scale), int(map_pixmap.height() * scale))
            map_matrix = qImage_to_npArray(map_pixmap.toImage())
            offset_col = int(offset[0] * scale) 
            offset_row = int(offset[1] * scale)
            print(f"offset_col: {offset_col}, offset_row: {offset_row}, map_matrix shape: {map_matrix.shape}, origin_matrix shape: {origin_matrix.shape}")
            if origin_matrix[offset_row:offset_row + map_matrix.shape[0], offset_col:offset_col + map_matrix.shape[1]].shape == map_matrix.shape:
                origin_matrix[offset_row:offset_row + map_matrix.shape[0], offset_col:offset_col + map_matrix.shape[1]] = map_matrix
            origin_pixmap = QPixmap.fromImage(npArray_to_qImage(origin_matrix))
            self.layer_maps[i]["pixmap"] = origin_pixmap

    def get_one_layer_map(self, layer_num, left_top:tuple, length_width, length_height):
        if len(self.pixItem_list) == 0:
            return
        pixItem =  self.pixItem_list.pop()
        my_left_top = pixItem.pos()
        scene = pixItem.scene()
        scene.removeItem(pixItem)
        pixmap = pixItem.pixmap()
        # 转换成numpy
        matrix = qImage_to_npArray(pixmap.toImage())
        alpha = matrix[:,:,3]
        # 连通域函数
        num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(alpha, connectivity=8, ltype=None)
        skip_label = 0
        for m in range(len(alpha)):
            flag = False
            for n in range(len(alpha[0])):
                if alpha[m][n] == 0:
                    skip_label = labels[m][n]
                    flag = True
                    break
            if flag:
                break
        for i in range(num_labels):
            x, y, w, h, area = stats[i]
            if i != skip_label:
                new_pixmap = pixmap.copy(x, y, w, h)
                layer_map = dict()
                layer_map["layer_num"] = layer_num
                layer_map["left_top"] = (left_top[0] + x / length_width, left_top[1] + y / length_height)
                layer_map["pixmap"] = new_pixmap
                self.layer_maps.append(layer_map)
                layer_transform = dict()
                layer_transform['scale'] = 1.
                layer_transform['offset'] = (0., 0.)
                self.layer_transforms.append(layer_transform)
                new_pixItem = QGraphicsPixmapItem(new_pixmap)
                new_pixItem.setZValue(1)
                new_pixItem.setPos(QPointF(x, y) + my_left_top)
                scene.addItem(new_pixItem)
                self.pixItem_list.append(new_pixItem)
        
    def save(self, path, origin_width, origin_height, scale):
        # 创建一个origin_width * origin_height的矩阵
        metadata = []
        origin_height = int(origin_height / (2 ** scale))
        origin_width = int(origin_width / (2 ** scale))
        origin_matrix = np.zeros((origin_height, origin_width), dtype=np.uint8)
        # print(f"origin_matrix shape: {origin_matrix.shape}")
        for i, layer_map in enumerate(self.layer_maps):
            current_layer = layer_map["layer_num"]
            left_top = layer_map["left_top"]
            pixmap = layer_map["pixmap"]
            # print(f"current_layer: {current_layer}, left_top: {left_top}")
            scale_rate = 2 ** (current_layer - scale)
            width = pixmap.width() * scale_rate
            height = pixmap.height() * scale_rate
            # print(f"width: {width}, height: {height}")
            pixmap = pixmap.scaled(int(width), int(height))
            # print(f"pixmap width: {pixmap.width()}, pixmap height: {pixmap.height()}")
            matrix = qImage_to_npArray(pixmap.toImage())
            # print(f"matrix shape: {matrix.shape}")
            alpha = matrix[:,:,3]
            offset_row = int(left_top[1] * origin_height)
            offset_col = int(left_top[0] * origin_width)
            # print(f"offset_row: {offset_row}, offset_col: {offset_col}")
            # print(f"alpha shape: {alpha.shape}")
            origin_matrix[offset_row:offset_row + alpha.shape[0], offset_col:offset_col + alpha.shape[1]] = cv2.bitwise_or(origin_matrix[offset_row:offset_row + alpha.shape[0], offset_col:offset_col + alpha.shape[1]], alpha)
            # print(f"finish: {i}")
        # 连通域分析
        num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(origin_matrix, connectivity=8, ltype=None)
        skip_label = 0
        for m in range(len(origin_matrix)):
            flag = False
            for n in range(len(origin_matrix[0])):
                if origin_matrix[m][n] == 0:
                    skip_label = labels[m][n]
                    flag = True
                    break
            if flag:
                break
        # print(f"skip_label: {skip_label}, num_labels: {num_labels}")
        for i in range(num_labels):
            x, y, w, h, area = stats[i]
            if i != skip_label and area > 100:
                sub_matrix = origin_matrix[y:y+h, x:x+w].copy()
                mask = np.where((labels[y:y+h, x:x+w] != i) & (labels[y:y+h, x:x+w] != 0))
                sub_matrix[mask] = 0

                # 二值化
                sub_matrix = cv2.threshold(sub_matrix, 0, 255, cv2.THRESH_BINARY)[1]
                if not dContoursIsClose(sub_matrix):
                    continue
                cv2.imwrite(os.path.join(path, f'{i}.jpg'), sub_matrix)
                meta = {"name":f"{i}.jpg", "left_top": (x / origin_width, y / origin_height)}
                metadata.append(meta)
        return metadata
    
    def load(self, left_top, img, scene, layer_num, save_scale_rate):
        # 设置缩放比例
        scale = 2 ** (layer_num - save_scale_rate)
        # img = cv2.resize(img, (int(img.shape[1] * scale), int(img.shape[0] * scale)))
        rgb = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
        color = color_table[self.type]
        non_zero_alpha = img > 0
        rgb[non_zero_alpha] = color.getRgb()[:3]
        rgba = np.concatenate([rgb, img[:,:,np.newaxis]], axis=2)
        # rgba = cv2.resize(rgba, (int(img.shape[1] * scale), int(img.shape[0] * scale)))
        pixmap = QPixmap.fromImage(npArray_to_qImage(rgba))
        # 创建pixItem
        empty_pixItem = QGraphicsPixmapItem(QPixmap(1, 1))
        empty_pixItem.setZValue(1)
        scene.addItem(empty_pixItem)
        self.pixItem_list.append(empty_pixItem)
        # 创建layer_map
        layer_map = dict()
        layer_map["layer_num"] = save_scale_rate
        layer_map["left_top"] = left_top
        layer_map["pixmap"] = pixmap
        self.layer_maps.append(layer_map)
        # 创建layer_transform
        layer_transform = dict()
        layer_transform['scale'] = scale
        layer_transform['offset'] = (0., 0.)
        self.layer_transforms.append(layer_transform)
            
        

class MyWorkSpace(QWidget, WorkSpaceUi):
    back_signal = pyqtSignal()
    unsave_signal = pyqtSignal(bool)
    def __init__(self, image_path, receiver=None):
        super().__init__()
        self.setupUi(self)
        self.file_path = image_path
        self.images = Graph(image_path)
        self.scene = QGraphicsScene()
        self.pixel_item = QGraphicsPixmapItem()
        self.pixel_item.setZValue(0)
        self.base_scale = 1.0
        self.graphicsView.setScene(self.scene)
        self.graphicsView.show()
        self.toolButton_8.clicked.connect(self.zoom_in)
        self.toolButton_9.clicked.connect(self.zoom_out)
        # 绘图相关
        self.current_layer_num = None
        self.graph_layers = dict()
        self.pixmap_item = None
        self.update_image()
        self.fit_view()
        # 信号
        self.leftward.clicked.connect(self.expand_left)
        self.rightward.clicked.connect(self.expand_right)
        self.upward.clicked.connect(self.expand_top)
        self.downward.clicked.connect(self.expand_bottom)

        self.toolButton.clicked.connect(lambda checked, b=self.toolButton: self.painter(b))
        self.toolButton_4.clicked.connect(lambda checked, b=self.toolButton_4: self.magic_pen(b))
        self.toolButton_2.clicked.connect(lambda checked, b=self.toolButton_2: self.eraser(b))
        self.toolButton_3.clicked.connect(lambda checked, b=self.toolButton_3: self.scissor(b))
        # 禁用4个方向的按钮
        self.leftward.setEnabled(False)
        self.rightward.setEnabled(False)
        self.upward.setEnabled(False)
        self.downward.setEnabled(False)
        # 设置透明度
        self.leftward.setStyleSheet("QPushButton { background-color: transparent; }")
        self.rightward.setStyleSheet("QPushButton { background-color: transparent; }")
        self.upward.setStyleSheet("QPushButton { background-color: transparent; }")
        self.downward.setStyleSheet("QPushButton { background-color: transparent; }")
        # 定时检查
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.handle_timeout)
        self.timer.start(500)  # 每隔1秒触发一次
        # 当前、上一个选中的工具
        self.current_tool = None
        # 鼠标轨迹
        self.graphicsView.installEventFilter(self)
        self.graphicsView.viewport().installEventFilter(self)
        # 手势
        self.grabGesture(Qt.GestureType.PinchGesture)
        # 浮动窗口
        self.dockWidget.setVisible(False)
        self.dockWidget.setFloating(True)
        self.dockWidget.setWindowTitle("图层")
        self.toolButton_5.clicked.connect(self.open_float_window)
        self.addLayer.clicked.connect(self.add_layer)
        self.idx = 0
        self.DiseasedArea.itemClicked.connect(self.on_item_clicked)
        # 历史记录
        # self.undo.clicked.connect(self.on_undo)
        # self.redo.clicked.connect(self.on_redo)
        self.history_len = 0
        # 后台处理程序
        self.input_queue = Queue()
        self.output_queue = Queue()
        # self.process = Process(target=layer_map_process, args=(self.input_queue, self.output_queue))
        # self.process.start()
        # 画笔宽度
        self.pen_width = 3
        self.erase_width = 10
        self.penSlider.setVisible(False)
        self.eraseSlider.setVisible(False)
        self.comboBox.addItems(["完整的", "不完整的"])
        self.comboBox.setVisible(False)
        self.penSlider.valueChanged.connect(self.on_pen_width_changed)
        self.eraseSlider.valueChanged.connect(self.on_erase_width_changed)
        # 保存
        self.toolButton_7.clicked.connect(self.save_as)
        self.toolButton_6.clicked.connect(self.save)
        self.unsave_signal.connect(receiver.unsaved_warning)
        self.save_scale_rate = None
        # 读取metadata
        with open(os.path.join(self.file_path, 'metadata.json'), 'r', encoding="utf-8") as f:
            import json
            data = json.load(f)
            self.save_scale_rate = data['save_scale_rate']
        # 返回上一级
        if receiver is not None:
            self.back_signal.connect(receiver.back_to_start)
            self.home.clicked.connect(lambda: self.back_signal.emit())
        # 加载
        self.load()

    # def event(self, event: QEvent):
    #     if event.type() == QEvent.Type.Gesture:
    #         return self.gestureEvent(QGestureEvent(event))
    #     return QWidget.event(event)

    def open_float_window(self):
        self.dockWidget.setVisible(not self.dockWidget.isVisible())
        # 设置位置
        if self.dockWidget.isVisible():
            global_pos = self.parentWidget().geometry().bottomRight()
            print(f"global_pos: {global_pos}")
            x = global_pos.x() - self.dockWidget.width() - 50
            y = self.parentWidget().geometry().top() + self.parentWidget().geometry().height() // 2 - self.dockWidget.height() // 2
            self.dockWidget.move(QPoint(x, y))



    def zoom_in(self):
        self.graphicsView.scale(1.2, 1.2)
        current_start, current_end, current_top_left, current_bottom_right, ran = self.on_scale_changed()
        # 总共区域的块数
        block_num = self.images.get_block_num()
        if ran < 1 / (4.0 * block_num) and self.images.current_layer > 0:
            center, relative_pos = self.get_center_pos()
            old_start = self.images.current_img[0]
            self.images.current_layer -= 1
            # self.images.current_img = [(current_start[0] * 2, current_start[1] *2), (current_end[0] * 2, current_end[1] * 2)]
            if current_end[0] - current_start[0] == 1:
                self.images.current_img[0] = (current_start[0] * 2, self.images.current_img[0][1])  
                self.images.current_img[1] = (current_end[0] * 2, self.images.current_img[1][1])
            else:
                self.images.current_img[0] = (current_start[0] * 2 + 1, self.images.current_img[0][1])
                self.images.current_img[1] = (current_end[0] * 2 - 1, self.images.current_img[1][1])

            if current_end[1] - current_start[1] == 1:
                self.images.current_img[0] = (self.images.current_img[0][0], current_start[1] * 2)
                self.images.current_img[1] = (self.images.current_img[1][0], current_end[1] * 2)
            else:
                self.images.current_img[0] = (self.images.current_img[0][0], current_start[1] * 2 + 1)
                self.images.current_img[1] = (self.images.current_img[1][0], current_end[1] * 2 - 1)
            # 计算新的宽高 
            start = self.images.current_img[0]
            end = self.images.current_img[1]
            width, height = end[1] - start[1], end[0] - start[0]
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (start[1] / len(self.images.layers[self.images.current_layer][0]), start[0] / len(self.images.layers[self.images.current_layer]))
            new_width, new_height = width * self.images.width, height * self.images.height
            for (layer, _) in self.graph_layers.values():
                layer.transform(self.scene,new_width, new_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            self.update_image()
            self.graphicsView.scale(0.5, 0.5)
            center = (center[0] * 2, center[1] * 2)

            # 获取pixItem的边界
            item_rect_scene = self.pixmap_item.boundingRect()
            center_left_top = QPointF((center[1] - start[1]) / width * item_rect_scene.width(), (center[0] - start[0]) / height * item_rect_scene.height()) + item_rect_scene.topLeft()
            center_pos = QPointF(center_left_top + QPointF(relative_pos[0] * 2 * self.images.width, relative_pos[1] * 2 * self.images.height))
            # 移动到中心
            self.graphicsView.centerOn(center_pos)
            print(f"change_current_layer_to: {self.images.current_layer}")
            print(f"start: {(current_start[0] * 2, current_start[1] *2)}, end: {(current_end[0] * 2, current_end[1] * 2)}")
                     
    def zoom_out(self):
        self.graphicsView.scale(0.8, 0.8)
        current_start, current_end, current_top_left, current_bottom_right, ran = self.on_scale_changed()
        if ran > 0.99 and self.images.current_layer < len(self.images.layers) - 1:
            length_y = len(self.images.layers[self.images.current_layer])
            length_x = len(self.images.layers[self.images.current_layer][0])
            center, relative_pos = self.get_center_pos()
            self.images.current_layer += 1
            self.images.current_img = [(clamp(current_start[0]-1, 0, length_y) // 2, clamp(current_start[1]-1, 0, length_x) // 2), (clamp(current_end[0]+1,0,length_y) // 2, clamp(current_end[1]+1,0,length_x) // 2)]
            start = self.images.current_img[0]
            end = self.images.current_img[1]
            width, height = end[1] - start[1], end[0] - start[0]
            new_width, new_height = width * self.images.width, height * self.images.height
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (start[1] / len(self.images.layers[self.images.current_layer][0]), start[0] / len(self.images.layers[self.images.current_layer]))
            
            for (layer, _) in self.graph_layers.values():
                layer.transform(self.scene,new_width, new_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            
            self.update_image()
            self.graphicsView.scale(2, 2)
            new_center = (center[0] // 2, center[1] // 2)
            remain_top = center[0] % 2
            remain_left = center[1] % 2

            # 获取pixItem的边界
            item_rect_scene = self.pixmap_item.boundingRect()
            center_left_top = QPointF((new_center[1] - start[1]) / width * item_rect_scene.width() + remain_left * 0.5 * item_rect_scene.width(), (new_center[0] - start[0]) / height * item_rect_scene.height() + remain_top * 0.5 * item_rect_scene.height()) + item_rect_scene.topLeft()
            center_pos = QPointF(center_left_top + QPointF(relative_pos[0] * 0.5 * self.images.width, relative_pos[1] * 0.5 * self.images.height))
            # 移动到中心
            self.graphicsView.centerOn(center_pos)
        
        if ran > 0.99 and self.images.current_layer == len(self.images.layers) - 1:
            self.expand_bottom1()
            self.expand_right1()
            self.expand_top1()
            self.expand_left1()
            

    def get_center_pos(self):
        view_point = QPoint(self.graphicsView.width()//2, self.graphicsView.height()//2)
        scene_point = self.graphicsView.mapToScene(view_point)
        # 获取pixItem的边界
        item_rect_scene = self.pixmap_item.boundingRect()
        # 显示归一化坐标
        relative_center = (scene_point.x() / item_rect_scene.width(), scene_point.y() / item_rect_scene.height())
        # 映射到取值范围中    
        start = self.images.current_img[0]
        end = self.images.current_img[1]
        width, height = end[1] - start[1], end[0] - start[0]
        new_center = (int(start[0] + relative_center[1] * height),int(start[1] + relative_center[0] * width))
        # 计算选中区域的top_left
        new_center_top_left = QPointF((new_center[1] - start[1]) / width * item_rect_scene.width(), (new_center[0] - start[0]) / height * item_rect_scene.height()) + item_rect_scene.topLeft()
        # 计算中心点到左上角的距离
        ditance = QPointF(scene_point - new_center_top_left)
        # 归一化到0-1
        relative_pos = (ditance.x() / self.images.width, ditance.y() / self.images.height)
        return  new_center, relative_pos  

    def on_scale_changed(self):
        
        # 将窗口坐标转换为视图坐标
        view_point_lt = QPoint(0, 0)
        view_point_rb = QPoint(self.graphicsView.width(), self.graphicsView.height())

        # 将视图坐标转换为场景坐标
        scene_point_lt = self.graphicsView.mapToScene(view_point_lt)
        scene_point_rb = self.graphicsView.mapToScene(view_point_rb)

        # 获取pixItem的边界
        item_rect_scene = self.pixmap_item.boundingRect()
        # 显示归一化坐标
        relative_top_left = (scene_point_lt.x() / item_rect_scene.width(), scene_point_lt.y() / item_rect_scene.height())
        relative_bottom_right = (scene_point_rb.x() / item_rect_scene.width(), scene_point_rb.y() / item_rect_scene.height())
        relative_top_left = (clamp(relative_top_left[0], 0, 0.999), clamp(relative_top_left[1], 0, 0.999))
        relative_bottom_right = (clamp(relative_bottom_right[0], 0, 0.999), clamp(relative_bottom_right[1], 0, 0.999))
        # 判断面积占比
        ran = (relative_bottom_right[0] - relative_top_left[0]) * (relative_bottom_right[1] - relative_top_left[1])
        # 映射到取值范围中    
        start = self.images.current_img[0]
        end = self.images.current_img[1]
        width, height = end[1] - start[1], end[0] - start[0]
        new_start = (int(start[0] + relative_top_left[1] * height),int(start[1] + relative_top_left[0] * width))
        new_end = (int(start[0] + relative_bottom_right[1] * height)+1, int(start[1] + relative_bottom_right[0] * width)+1)
        # 计算选中区域的top_left和bottom_right
        new_top_left = QPointF((new_start[1] - start[1]) / width * item_rect_scene.width(), (new_start[0] - start[0]) / height * item_rect_scene.height()) + item_rect_scene.topLeft()
        new_bottom_right = QPointF((new_end[1] - start[1]) / width * item_rect_scene.width(), (new_end[0] - start[0]) / height * item_rect_scene.height()) + item_rect_scene.topLeft()
        return new_start, new_end, new_top_left, new_bottom_right, ran


    def update_image(self, **kwargs):
        self.scene.clear()  # 先清空上次的残留
        pix = QPixmap.fromImage(self.generate_image())
        self.pixmap_item = QGraphicsPixmapItem(pix)
        self.pixmap_item.setZValue(0)
        # self.pixmap_item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable, True)
        if 'pos' in kwargs:
            self.pixmap_item.setPos(kwargs['pos'])
        # 设置场景大小与 QGraphicsPixmapItem 大小一致
        pixmap_item_rect = self.pixmap_item.boundingRect()
        self.scene.setSceneRect(pixmap_item_rect)
        self.scene.addItem(self.pixmap_item)
        for (layer, _) in self.graph_layers.values():
            for pixItem in layer.pixItem_list:
                # print(f"pixItem size: {pixItem.pixmap().size()}")
                self.scene.addItem(pixItem)
        # 将场景添加到视图
        self.graphicsView.setScene(self.scene)

    def update_image_with_same_scale(self, **kwargs):
        '''
        在相同的缩放层次下进行更新，不用删除所有的pixItem
        '''
        # 更新当前的图片
        origin_matrix = qImage_to_npArray(self.pixmap_item.pixmap().toImage())
        origin_matrix = cv2.cvtColor(origin_matrix, cv2.COLOR_BGRA2RGB)
        # 记录原来的patch编号
        origin_start = kwargs['origin_start']
        origin_end = kwargs['origin_end']
        # 记录新的patch编号
        new_start = kwargs['new_start']
        new_end = kwargs['new_end']
        # 取交集
        start = (max(origin_start[0], new_start[0]), max(origin_start[1], new_start[1]))
        end = (min(origin_end[0], new_end[0]), min(origin_end[1], new_end[1]))
        # 新矩阵
        QImageReader.setAllocationLimit(104857600)
        varrays = []
        for i in range(new_start[0], new_end[0]):
            harrays = []
            for j in range(new_start[1], new_end[1]):
                if i >= start[0] and i < end[0] and j >= start[1] and j < end[1]:
                    harrays.append(origin_matrix[(i - origin_start[0]) * self.images.height:(i - origin_start[0]+1) * self.images.height, (j - origin_start[1]) * self.images.width:(j - origin_start[1]+1) * self.images.width])
                else:
                    img = self.images.layers[self.images.current_layer][i][j]
                    harrays.append(cv2.imread(img))
            varrays.append(np.hstack(harrays))
        
        img = np.vstack(varrays)
        # 将图片转换为QImage
        image_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 转换为 RGB 格式
        # 将图像数据转换为 QImage
        height, width, channels = image_rgb.shape
        bytes_per_line = channels * width
        q_image = QImage(image_rgb.data, width, height, bytes_per_line, QImage.Format.Format_RGB888)

        pix = QPixmap.fromImage(q_image)
        self.scene.removeItem(self.pixmap_item)
        self.pixmap_item = QGraphicsPixmapItem(pix)
        self.pixmap_item.setZValue(0)
        pixmap_item_rect = self.pixmap_item.boundingRect()
        self.scene.setSceneRect(pixmap_item_rect)
        self.scene.addItem(self.pixmap_item)
        for (layer, _) in self.graph_layers.values():
            for pixItem in layer.pixItem_list:
                # print(f"pixItem size: {pixItem.pixmap().size()}")
                self.scene.addItem(pixItem)
        # 将场景添加到视图
        self.graphicsView.setScene(self.scene)


    def generate_image(self):
        QImageReader.setAllocationLimit(104857600)
        start = self.images.current_img[0]
        end = self.images.current_img[1]
        # if start == end:
        #     q_image = QImage(self.images.layers[self.images.current_layer][start[0]][start[1]])
        #     return q_image
        varrays = []
        for i in range(start[0], end[0]):
            harrays = []
            for j in range(start[1], end[1]):
                img = self.images.layers[self.images.current_layer][i][j]
                harrays.append(cv2.imread(img))
            varrays.append(np.hstack(harrays))
        
        img = np.vstack(varrays)
        # 将图片转换为QImage
        image_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 转换为 RGB 格式

        # 将图像数据转换为 QImage
        height, width, channels = image_rgb.shape
        bytes_per_line = channels * width
        q_image = QImage(image_rgb.data, width, height, bytes_per_line, QImage.Format.Format_RGB888)
        return q_image  

    def fit_view(self):
        # 设置场景大小与 QGraphicsPixmapItem 大小一致
        pixmap_item_rect = self.pixmap_item.boundingRect()
        width, height = pixmap_item_rect.width(), pixmap_item_rect.height()
        # 计算缩放比例
        if width >= height:
            scale = self.graphicsView.width() / width
        else:
            scale = self.graphicsView.height() / height
        self.base_scale = scale
        self.graphicsView.scale(scale, scale)

    def resizeEvent(self, event):
        # 调用基类的 resizeEvent 方法，处理原始的事件
        super().resizeEvent(event)

        # 获取新的窗口大小
        new_size = event.size()
        old_size = event.oldSize()
        old_width, old_height = old_size.width(), old_size.height()
        offset_pen_slider_x = old_width // 2 - self.penSlider.geometry().x()
        offset_erase_slider_x = old_width // 2 - self.eraseSlider.geometry().x()
        offset_combo_box_x = old_width // 2 - self.comboBox.geometry().x()

        # 在这里可以添加任何需要在窗口大小变化时执行的代码
        # 例如，更新布局，重新绘制界面元素等
        self.graphicsView.setGeometry(0, 0, new_size.width(), new_size.height())
        # 修改toobox的位置
        width = self.toolBox.width()
        height = self.toolBox.height()
        posy = self.toolBox.y()
        self.toolBox.setGeometry(new_size.width()//2- width // 2, posy, width, height)
        # 修改upward的位置
        width = self.upward.width()
        height = self.upward.height()
        posy = self.upward.y()
        self.upward.setGeometry(new_size.width()//2- width // 2, posy, width, height)
        # 修改downward的位置
        width = self.downward.width()
        height = self.downward.height()
        posy = new_size.height() - 40
        self.downward.setGeometry(new_size.width()//2- width // 2, posy, width, height)
        # 修改leftward的位置
        width = self.leftward.width()
        height = self.leftward.height()
        posx = self.leftward.x()
        self.leftward.setGeometry(posx, new_size.height()//2 - height // 2, width, height)
        # 修改rightward的位置
        width = self.rightward.width()
        height = self.rightward.height()
        posx = new_size.width() - 50
        self.rightward.setGeometry(posx, new_size.height()//2 - height // 2, width, height)
        # 修改penSlider的位置
        if old_size.width() != -1:
            width = self.penSlider.width()
            height = self.penSlider.height()
            poxy = self.penSlider.geometry().y()
            self.penSlider.setGeometry(new_size.width() // 2 - offset_pen_slider_x  , poxy, width, height)
            # 修改eraseSlider的位置
            width = self.eraseSlider.width()
            height = self.eraseSlider.height()
            poxy = self.eraseSlider.geometry().y()
            self.eraseSlider.setGeometry(new_size.width() // 2 - offset_erase_slider_x  , poxy, width, height)

            width = self.comboBox.width()
            height = self.comboBox.height()
            poxy = self.comboBox.geometry().y()
            self.comboBox.setGeometry(new_size.width() // 2 - offset_combo_box_x  , poxy, width, height)


    def handle_timeout(self):
        pass
        # # print(f"center: {center}, relative_pos: {relative_pos}")
        # at_left, at_right, at_top, at_bottom = self.is_at_edge()
        # # print(f"at_left: {at_left}, at_right: {at_right}, at_top: {at_top}, at_bottom: {at_bottom}")
        # if at_left and self.images.current_img[0][1] > 0:
        #     # self.leftward.setStyleSheet("QPushButton { background-color: #cccbc8; }")
        #     # self.leftward.setEnabled(True)
        #     self.expand_left()
        #     return
        # else:
        #     self.leftward.setStyleSheet("QPushButton { background-color: transparent; }")
        #     self.leftward.setEnabled(False)

        # if at_right and self.images.current_img[1][1] + 1 <= len(self.images.layers[self.images.current_layer][0]):
        #     # self.rightward.setStyleSheet("QPushButton { background-color: #cccbc8; }")
        #     # self.rightward.setEnabled(True)
        #     self.expand_right()
        #     return
        # else:
        #     self.rightward.setStyleSheet("QPushButton { background-color: transparent; }")
        #     self.rightward.setEnabled(False)

        # if at_top and self.images.current_img[0][0] > 0:
        #     # self.upward.setStyleSheet("QPushButton { background-color: #cccbc8; }")
        #     # self.upward.setEnabled(True)
        #     self.expand_top()
        #     return
        # else:
        #     self.upward.setStyleSheet("QPushButton { background-color: transparent; }")
        #     self.upward.setEnabled(False)

        # if at_bottom and self.images.current_img[1][0] + 1 <= len(self.images.layers[self.images.current_layer]):
        #     # self.downward.setStyleSheet("QPushButton { background-color: #cccbc8; }")
        #     # self.downward.setEnabled(True)
        #     self.expand_bottom()
        #     return
        # else:
        #     self.downward.setStyleSheet("QPushButton { background-color: transparent; }")
        #     self.downward.setEnabled(False)

    def is_at_edge(self):
        # 获取视图当前可见区域的矩形（在场景坐标系中的表示）
        visible_scene_rect = self.graphicsView.mapToScene(self.graphicsView.viewport().rect()).boundingRect()
        
        # 获取场景矩形
        scene_rect = self.scene.sceneRect()

        # 检查是否滚动到任意边界
        at_left = visible_scene_rect.left() <= scene_rect.left() + 1
        # print(f"visible_scene_rect.left(): {visible_scene_rect.left()}, scene_rect.left(): {scene_rect.left()}")
        at_right = visible_scene_rect.right() >= scene_rect.right() - 1
        # print(f"visible_scene_rect.right(): {visible_scene_rect.right()}, scene_rect.right(): {scene_rect.right()}")
        at_top = visible_scene_rect.top() <= scene_rect.top() + 1
        # print(f"visible_scene_rect.top(): {visible_scene_rect.top()}, scene_rect.top(): {scene_rect.top()}")
        at_bottom = visible_scene_rect.bottom() >= scene_rect.bottom() - 1
        # print(f"visible_scene_rect.bottom(): {visible_scene_rect.bottom()}, scene_rect.bottom(): {scene_rect.bottom()}")

        return at_left, at_right, at_top, at_bottom

    def expand_left(self):
        if self.images.current_img[0][1] > 0:
            # 获取当前中心坐标
            view_point = QPoint(self.graphicsView.width()//2, self.graphicsView.height()//2)
            scene_point = self.graphicsView.mapToScene(view_point)
            # print(f"center: {center}, relative_pos: {relative_pos}")
            self.images.current_img[0] = (self.images.current_img[0][0], self.images.current_img[0][1] - 1)
            self.images.current_img[1] = (self.images.current_img[1][0], self.images.current_img[1][1] - 1)
            origin_width, origin_height = self.images.width * (self.images.current_img[1][1] - self.images.current_img[0][1]), self.images.height * (self.images.current_img[1][0] - self.images.current_img[0][0])
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (self.images.current_img[0][1] / len(self.images.layers[self.images.current_layer][0]), self.images.current_img[0][0] / len(self.images.layers[self.images.current_layer]))
            for (layer,_) in self.graph_layers.values():
                layer.transform(self.scene,origin_width, origin_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            self.update_image_with_same_scale(origin_start=(self.images.current_img[0][0], self.images.current_img[0][1] + 1), origin_end=(self.images.current_img[1][0], self.images.current_img[1][1] + 1), new_start=(self.images.current_img[0][0], self.images.current_img[0][1]), new_end=(self.images.current_img[1][0], self.images.current_img[1][1]))
            # 计算新的位置
            scene_point = scene_point + QPointF(self.images.width, 0)
            self.graphicsView.centerOn(scene_point)

    def expand_left1(self):
        if self.images.current_img[0][1] > 0:
            # 获取当前中心坐标
            view_point = QPoint(self.graphicsView.width()//2, self.graphicsView.height()//2)
            scene_point = self.graphicsView.mapToScene(view_point)
            # print(f"center: {center}, relative_pos: {relative_pos}")
            self.images.current_img[0] = (self.images.current_img[0][0], self.images.current_img[0][1] - 1)
            self.images.current_img[1] = (self.images.current_img[1][0], self.images.current_img[1][1])
            origin_width, origin_height = self.images.width * (self.images.current_img[1][1] - self.images.current_img[0][1]), self.images.height * (self.images.current_img[1][0] - self.images.current_img[0][0])
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (self.images.current_img[0][1] / len(self.images.layers[self.images.current_layer][0]), self.images.current_img[0][0] / len(self.images.layers[self.images.current_layer]))
            for (layer,_) in self.graph_layers.values():
                layer.transform(self.scene,origin_width, origin_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            self.update_image()
            # 计算新的位置
            scene_point = scene_point + QPointF(self.images.width, 0)
            self.graphicsView.centerOn(scene_point)

    def expand_right(self):
        if self.images.current_img[1][1] + 1 <= len(self.images.layers[self.images.current_layer][0]):
            # 获取当前中心坐标
            view_point = QPoint(self.graphicsView.width()//2, self.graphicsView.height()//2)
            scene_point = self.graphicsView.mapToScene(view_point)
            # print(f"center: {center}, relative_pos: {relative_pos}")
            self.images.current_img[0] = (self.images.current_img[0][0], self.images.current_img[0][1] + 1)
            self.images.current_img[1] = (self.images.current_img[1][0], self.images.current_img[1][1] + 1)
            origin_width, origin_height = self.images.width * (self.images.current_img[1][1] - self.images.current_img[0][1]), self.images.height * (self.images.current_img[1][0] - self.images.current_img[0][0])
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (self.images.current_img[0][1] / len(self.images.layers[self.images.current_layer][0]), self.images.current_img[0][0] / len(self.images.layers[self.images.current_layer]))
            for (layer,_) in self.graph_layers.values():
                layer.transform(self.scene,origin_width, origin_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            self.update_image_with_same_scale(origin_start=(self.images.current_img[0][0], self.images.current_img[0][1] - 1), origin_end=(self.images.current_img[1][0], self.images.current_img[1][1] - 1), new_start=(self.images.current_img[0][0], self.images.current_img[0][1]), new_end=(self.images.current_img[1][0], self.images.current_img[1][1]))
            # 计算center的位置
            scene_point = scene_point - QPointF(self.images.width, 0)
            self.graphicsView.centerOn(scene_point)

    def expand_right1(self):
        if self.images.current_img[1][1] + 1 <= len(self.images.layers[self.images.current_layer][0]):
            # 获取当前中心坐标
            view_point = QPoint(self.graphicsView.width()//2, self.graphicsView.height()//2)
            scene_point = self.graphicsView.mapToScene(view_point)
            # print(f"center: {center}, relative_pos: {relative_pos}")
            self.images.current_img[0] = (self.images.current_img[0][0], self.images.current_img[0][1])
            self.images.current_img[1] = (self.images.current_img[1][0], self.images.current_img[1][1] + 1)
            origin_width, origin_height = self.images.width * (self.images.current_img[1][1] - self.images.current_img[0][1]), self.images.height * (self.images.current_img[1][0] - self.images.current_img[0][0])
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (self.images.current_img[0][1] / len(self.images.layers[self.images.current_layer][0]), self.images.current_img[0][0] / len(self.images.layers[self.images.current_layer]))            
            for (layer,_) in self.graph_layers.values():
                layer.transform(self.scene,origin_width, origin_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            self.update_image()
            # 计算center的位置
            scene_point = scene_point - QPointF(self.images.width, 0)
            self.graphicsView.centerOn(scene_point)

    def expand_top(self):
        if self.images.current_img[0][0] > 0:
            # 获取当前中心坐标
            view_point = QPoint(self.graphicsView.width()//2, self.graphicsView.height()//2)
            scene_point = self.graphicsView.mapToScene(view_point)
            # print(f"center: {center}, relative_pos: {relative_pos}")
            self.images.current_img[0] = (self.images.current_img[0][0] - 1, self.images.current_img[0][1])
            self.images.current_img[1] = (self.images.current_img[1][0] - 1, self.images.current_img[1][1])
            origin_width, origin_height = self.images.width * (self.images.current_img[1][1] - self.images.current_img[0][1]), self.images.height * (self.images.current_img[1][0] - self.images.current_img[0][0])
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (self.images.current_img[0][1] / len(self.images.layers[self.images.current_layer][0]), self.images.current_img[0][0] / len(self.images.layers[self.images.current_layer]))
            for (layer,_) in self.graph_layers.values():
                layer.transform(self.scene,origin_width, origin_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            self.update_image_with_same_scale(origin_start=(self.images.current_img[0][0] + 1, self.images.current_img[0][1]), origin_end=(self.images.current_img[1][0] + 1, self.images.current_img[1][1]), new_start=(self.images.current_img[0][0], self.images.current_img[0][1]), new_end=(self.images.current_img[1][0], self.images.current_img[1][1]))
            # 计算center的位置
            scene_point = scene_point + QPointF(0, self.images.height)
            self.graphicsView.centerOn(scene_point)
        
    def expand_top1(self):
        if self.images.current_img[0][0] > 0:
            # 获取当前中心坐标
            view_point = QPoint(self.graphicsView.width()//2, self.graphicsView.height()//2)
            scene_point = self.graphicsView.mapToScene(view_point)
            # print(f"center: {center}, relative_pos: {relative_pos}")
            self.images.current_img[0] = (self.images.current_img[0][0] - 1, self.images.current_img[0][1])
            self.images.current_img[1] = (self.images.current_img[1][0], self.images.current_img[1][1])
            origin_width, origin_height = self.images.width * (self.images.current_img[1][1] - self.images.current_img[0][1]), self.images.height * (self.images.current_img[1][0] - self.images.current_img[0][0])
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (self.images.current_img[0][1] / len(self.images.layers[self.images.current_layer][0]), self.images.current_img[0][0] / len(self.images.layers[self.images.current_layer]))
            for (layer,_) in self.graph_layers.values():
                layer.transform(self.scene,origin_width, origin_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            self.update_image()
            # 计算center的位置
            scene_point = scene_point + QPointF(0, self.images.height)
            self.graphicsView.centerOn(scene_point)

    def expand_bottom(self):
        if self.images.current_img[1][0] + 1 <= len(self.images.layers[self.images.current_layer]):
            # 获取当前中心坐标
            view_point = QPoint(self.graphicsView.width()//2, self.graphicsView.height()//2)
            scene_point = self.graphicsView.mapToScene(view_point)
            # print(f"center: {center}, relative_pos: {relative_pos}")
            self.images.current_img[0] = (self.images.current_img[0][0] + 1, self.images.current_img[0][1])
            self.images.current_img[1] = (self.images.current_img[1][0] + 1, self.images.current_img[1][1])
            origin_width, origin_height = self.images.width * (self.images.current_img[1][1] - self.images.current_img[0][1]), self.images.height * (self.images.current_img[1][0] - self.images.current_img[0][0])
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (self.images.current_img[0][1] / len(self.images.layers[self.images.current_layer][0]), self.images.current_img[0][0] / len(self.images.layers[self.images.current_layer]))
            for (layer,_) in self.graph_layers.values():
                layer.transform(self.scene,origin_width, origin_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            self.update_image_with_same_scale(origin_start=(self.images.current_img[0][0] - 1, self.images.current_img[0][1]), origin_end=(self.images.current_img[1][0] - 1, self.images.current_img[1][1]), new_start=(self.images.current_img[0][0], self.images.current_img[0][1]), new_end=(self.images.current_img[1][0], self.images.current_img[1][1]))
            # 计算center的位置
            scene_point = scene_point - QPointF(0, self.images.height)
            self.graphicsView.centerOn(scene_point)

    def expand_bottom1(self):
        print(f"len{len(self.images.layers[self.images.current_layer])}")
        if self.images.current_img[1][0] + 1 <= len(self.images.layers[self.images.current_layer]):
            # 获取当前中心坐标
            view_point = QPoint(self.graphicsView.width()//2, self.graphicsView.height()//2)
            scene_point = self.graphicsView.mapToScene(view_point)
            # print(f"center: {center}, relative_pos: {relative_pos}")
            self.images.current_img[0] = (self.images.current_img[0][0], self.images.current_img[0][1])
            self.images.current_img[1] = (self.images.current_img[1][0] + 1, self.images.current_img[1][1])
            origin_width, origin_height = self.images.width * (self.images.current_img[1][1] - self.images.current_img[0][1]), self.images.height * (self.images.current_img[1][0] - self.images.current_img[0][0])
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            left_top = (self.images.current_img[0][1] / len(self.images.layers[self.images.current_layer][0]), self.images.current_img[0][0] / len(self.images.layers[self.images.current_layer]))
            for (layer,_) in self.graph_layers.values():
                layer.transform(self.scene,origin_width, origin_height, length_width, length_height, left_top, self.images.current_layer, self.output_queue)
            self.update_image()
            # 计算center的位置
            scene_point = scene_point - QPointF(0, self.images.height)
            self.graphicsView.centerOn(scene_point)

    def eventFilter(self, obj, event):
        if event.type() == QEvent.Type.MouseMove or event.type() == QEvent.Type.MouseButtonPress or event.type() == QEvent.Type.MouseButtonRelease:
            QApplication.sendEvent(self, event)
            return True
        if event.type() == QEvent.Type.Gesture:
            self.gestureEvent(QGestureEvent(event))
            return True
        return super().eventFilter(obj, event)

    def gestureEvent(self, event: QGestureEvent):
        for gesture in event.gestures():
            if isinstance(gesture, QPanGesture):
                delta = gesture.delta()
                # 打印平移的方向和距离
                print(f"Pan gesture detected. Delta: {delta.x()} {delta.y()}")

                
    
    def mousePressEvent(self, event):
        pass

    def wheelEvent(self, event):
        # 获取滚轮滚动的角度
        deltaX = event.angleDelta().x()
        deltaY = event.angleDelta().y()
        print(f"deltaX: {deltaX}, deltaY: {deltaY}")
        at_left, at_right, at_top, at_bottom = self.is_at_edge()
        if deltaX > 2 and at_left and self.images.current_img[0][1] > 0:
            # self.leftward.setStyleSheet("QPushButton { background-color: #cccbc8; }")
            # self.leftward.setEnabled(True)
            self.expand_left()
            return

        if deltaX < -2 and at_right and self.images.current_img[1][1] + 1 <= len(self.images.layers[self.images.current_layer][0]):
            # self.rightward.setStyleSheet("QPushButton { background-color: #cccbc8; }")
            # self.rightward.setEnabled(True)
            self.expand_right()
            return

        if deltaY > 2 and at_top and self.images.current_img[0][0] > 0:
            # self.upward.setStyleSheet("QPushButton { background-color: #cccbc8; }")
            # self.upward.setEnabled(True)
            self.expand_top()
            return

        if deltaY < -2 and at_bottom and self.images.current_img[1][0] + 1 <= len(self.images.layers[self.images.current_layer]):
            # self.downward.setStyleSheet("QPushButton { background-color: #cccbc8; }")
            # self.downward.setEnabled(True)
            self.expand_bottom()
            return
     
        
        # 阻止事件继续传播
        event.accept()

    def mouseMoveEvent(self, event):
        # print(f"mouseMoveEvent: {event.position()}")
        self.unsave_signal.emit(False)
        if self.current_tool == self.toolButton:
            graph_layer = self.graph_layers[self.current_layer_num][0]
            # 转换到图像坐标系
            pos = self.graphicsView.mapToScene(int(event.position().x()), int(event.position().y()))
            scale_rate = self.graphicsView.transform().m11()
            graph_layer.paint(pos, self.scene, scale_rate)
        elif self.current_tool == self.toolButton_2:
            # print(f"mousePressEvent: {event.position()}")
            graph_layer = self.graph_layers[self.current_layer_num][0]
            # 转换到图像坐标系
            pos = self.graphicsView.mapToScene(int(event.position().x()), int(event.position().y()))
            scale_rate = self.graphicsView.transform().m11()
            erase_type = self.comboBox.currentText()
            graph_layer.erase(pos, self.scene, scale_rate, self.input_queue, erase_type)
            

    def mouseReleaseEvent(self, event):
        if self.current_tool == self.toolButton:
            # print(f"release: {event.position()}")
            layer = self.graph_layers[self.current_layer_num][0]
            layer.last_pos = None
            start = self.images.current_img[0]
            left_top = (start[1] / len(self.images.layers[self.images.current_layer][0]), start[0] / len(self.images.layers[self.images.current_layer]))
            length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
            length_height = self.images.height * len(self.images.layers[self.images.current_layer])
            layer.get_one_layer_map(self.images.current_layer, left_top, length_width, length_height)
            self.history_len += 1
        elif self.current_tool == self.toolButton_2:
            layer = self.graph_layers[self.current_layer_num][0]
            layer.last_erase_pos = None
            layer.erase_mask = None
            layer.update_layer_map()

    def add_layer(self, type_name=None):
        item_rect_scene = self.pixmap_item.boundingRect()
        unique_id = uuid.uuid4()
        # print(f"unique_id: {unique_id}")
        graph_layer = DrawLayer(int(item_rect_scene.width()), int(item_rect_scene.height()), unique_id, self.pen_width, self.erase_width)
        self.current_layer_num = unique_id
        self.history_len = 0
        # 添加到dockedWidget
        item = QListWidgetItem()
        item.setSizeHint(QSize(0, 100))
        self.DiseasedArea.addItem(item)
        if type_name:
            widget = CustomListItem(f"图层{self.idx}", layer_num=unique_id, receiver=self,type_name=type_name)
            graph_layer.change_type(type_name, self.scene)
        else:
            widget = CustomListItem(f"图层{self.idx}", layer_num=unique_id, receiver=self)
        self.DiseasedArea.setItemWidget(item, widget)
        # 设置当前选中这个item
        self.DiseasedArea.setCurrentItem(item)
        self.idx += 1
        self.graph_layers[unique_id] = (graph_layer, widget)

    def init_layers(self):
        for type_name in level_list:
            self.add_layer(type_name)



    def on_signal_received(self, layer_num):
        # 删除图层
        # print(f"delete layer: {layer_num}")
        (layer, widget) = self.graph_layers.pop(layer_num)
        for pixItem in layer.pixItem_list:
            self.scene.removeItem(pixItem)

        # 删除dockedWidget中的item
        for i in range(self.DiseasedArea.count()):
            item = self.DiseasedArea.item(i)
            widget = self.DiseasedArea.itemWidget(item)
            if widget.layer_num == layer_num:
                self.DiseasedArea.takeItem(i)
                break
        
        if len(self.graph_layers) == 0:
            self.add_layer()
        else:
            for key in self.graph_layers.keys():
                self.current_layer_num = key
        self.history_len = 0

    def on_change_type_signal(self, layer_num, type_name):
        layer = self.graph_layers[layer_num][0]
        layer.change_type(type_name, self.scene)

    def painter(self, button: QPushButton):
        if self.current_tool == button:
            # 创建一个水平滑动条
            # self.penSlider.setVisible(not self.penSlider.isVisible())
            pass
        else:
            self.eraseSlider.setVisible(False)
            self.comboBox.setVisible(False)
            self.start_button_animation(button)
            pixmap = QPixmap(":/tools/brush-2.png")
            pixmap = pixmap.scaled(20, 20)
            cursor = QCursor(pixmap, 0, 16)
            self.graphicsView.setCursor(cursor)
            if len(self.graph_layers) == 0:
                self.add_layer()
    
    def on_pen_width_changed(self, value):
        for (layer, _) in self.graph_layers.values():
            layer.pen_width = value

    def on_erase_width_changed(self, value):
        for (layer, _) in self.graph_layers.values():
            layer.erase_width = value

    def magic_pen(self, button: QPushButton):
        if self.current_tool == button:
            return
        else:
            self.penSlider.setVisible(False)
            self.eraseSlider.setVisible(False)
            self.start_button_animation(button)

    def eraser(self, button: QPushButton):
        if self.current_tool == button:
            self.eraseSlider.setVisible(not self.eraseSlider.isVisible())
            self.comboBox.setVisible(not self.comboBox.isVisible())
        else:
            self.penSlider.setVisible(False)
            self.start_button_animation(button)
            pixmap = QPixmap(":/tools/eraser.png")
            pixmap = pixmap.scaled(20, 20)
            cursor = QCursor(pixmap, 0, 16)
            self.graphicsView.setCursor(cursor)

    def scissor(self, button: QPushButton):
        if self.current_tool == button:
            return
        else:
            self.penSlider.setVisible(False)
            self.eraseSlider.setVisible(False)
            self.start_button_animation(button)

    def start_button_animation(self, button: QPushButton):
        self.animation = QVariantAnimation(
            startValue=button.iconSize(),
            endValue=button.iconSize() * 1.2,
            duration=300,
            valueChanged=lambda value: button.setIconSize(value),  # 使用lambda函数
            easingCurve=QEasingCurve.Type.OutCubic
        )
        self.animation.start()
        if self.current_tool:
            self.current_tool.setIconSize(QSize(24, 24))
        self.current_tool = button

    def on_item_clicked(self, item):
        widget = self.DiseasedArea.itemWidget(item)
        self.current_layer_num = widget.layer_num
        self.history_len = 0

    def on_undo(self):
        pass
    
    def on_redo(self):
        pass

    def closeEvent(self, event):
        # self.input_queue.put({'idx': -1})
        # self.process.join()
        self.dockWidget.close()
        event.accept()

    def save(self, file_path=None):
        if not file_path:
            save_path = os.path.join(self.file_path, 'mask')
        else:
            save_path = os.path.join(file_path, 'mask')
        if not os.path.exists(save_path):
            os.makedirs(save_path)
        labels = os.listdir(save_path)
        for label in labels:
            if label == '.DS_Store':
                continue
            import shutil
            shutil.rmtree(os.path.join(save_path, label))
        width, height = self.images.get_origin_size()
        for (layer, widget) in self.graph_layers.values():
            # 获取widget的label文章
            label = str(layer.id)
            type_name = layer.type
            os.makedirs(os.path.join(save_path, label))
            metadata = layer.save(os.path.join(save_path, label),origin_width=width, origin_height=height, scale = self.save_scale_rate)
            if len(metadata) == 0:
                import shutil
                shutil.rmtree(os.path.join(save_path, label))
                continue 
            data = {
                'type': type_name,
                'metadata': metadata
            }
            
            with open(os.path.join(save_path, label, 'metadata.json'), 'w', encoding="utf-8") as f:
                import json
                json.dump(data, f)
        QMessageBox.information(self, "提示", "保存成功")

        self.unsave_signal.emit(True)
    def save_as(self):
        import sys
        fname,_=QFileDialog.getSaveFileName(self,'另存为',f"./{self.file_path.split('/')[-1].split('.')[0]}", options=QFileDialog.Option.ShowDirsOnly)
        if not fname:
            return
        import shutil
        if os.path.exists(fname):
            shutil.rmtree(fname) 
        os.makedirs(fname)

        lod_path = os.path.join(self.file_path, 'LOD')
        metadata_path = os.path.join(self.file_path, 'metadata.json')
        if os.path.exists(lod_path):
            shutil.copytree(lod_path, os.path.join(fname, 'LOD'))
        if os.path.exists(metadata_path):
            shutil.copy(metadata_path, os.path.join(fname, 'metadata.json'))
        self.save(fname)


    def load(self):
        load_path = os.path.join(self.file_path, 'mask')

        if not os.path.exists(load_path):
            # 消息框
            # QMessageBox.information(self, "提示", "没有mask文件夹", QMessageBox.Ok)
            return
        for label in os.listdir(load_path):
            # 判断名字为uuid4格式
            if label == '.DS_Store':
                continue
            if self.graph_layers.get(uuid.UUID(label)):
                continue
            import json
            # 获取metadata
            with open(os.path.join(load_path, label, 'metadata.json'), 'r', encoding="utf-8") as f:
                data = json.load(f)
                type_name = data['type']
                metadata = data['metadata']
                unique_id = uuid.UUID(label)
                item_rect_scene = self.pixmap_item.boundingRect()
                graph_layer = DrawLayer(int(item_rect_scene.width()), int(item_rect_scene.height()), unique_id, self.pen_width, self.erase_width)
                self.current_layer_num = unique_id
                self.history_len = 0
                # 添加到dockedWidget
                item = QListWidgetItem()
                item.setSizeHint(QSize(0, 100))
                self.DiseasedArea.addItem(item)
                widget = CustomListItem(f"图层{self.idx}", layer_num=unique_id, receiver=self)
                widget.set_type(type_name)
                self.DiseasedArea.setItemWidget(item, widget)
                self.idx += 1
                self.graph_layers[unique_id] = (graph_layer, widget)
                graph_layer.type = type_name
                for layer_map in metadata:
                    img_name = layer_map['name']
                    _, _, new_img = GRID().load_image(os.path.join(load_path, label, img_name))
                    
                    # img = new_img.T
                    left_top = layer_map['left_top']
                    graph_layer.load(left_top, new_img, self.scene, self.images.current_layer, self.save_scale_rate)
                length_width = self.images.width * len(self.images.layers[self.images.current_layer][0])
                length_height = self.images.height * len(self.images.layers[self.images.current_layer])
                canvas_left_top = (self.images.current_img[0][1] / len(self.images.layers[self.images.current_layer][0]), self.images.current_img[0][0] / len(self.images.layers[self.images.current_layer]))
                graph_layer.transform(self.scene, int(item_rect_scene.width()), int(item_rect_scene.height()), length_width, length_height, canvas_left_top, self.images.current_layer)
                for pixItem in graph_layer.pixItem_list:
                    self.scene.addItem(pixItem)    

                



if __name__ == "__main__":
    image_path = '/Users/gyj/project/computer_vision/Slice_Artboard/Output/Result-2024-04-18-061433A6046-20肝'
    import sys
    app = QApplication(sys.argv)
    # 打开并读取 QSS 文件
    file = QFile("/Users/gyj/project/computer_vision/Slice_Artboard/Resources/style.qss")
    file.open(QIODevice.OpenModeFlag.ReadOnly)
    styleSheet = file.readAll().data().decode('utf-8')
    # 将样式表应用到应用程序
    app.setStyleSheet(styleSheet)
    workspace = MyWorkSpace(image_path)
    workspace.show()
    sys.exit(app.exec())